﻿#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

//strerror
//char * strerror ( int errnum );
//返回错误码所对应的错误信息
//strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。
//错误码是一些数字：1 2 3 4 ……，我们需要将错误码翻译成错误信息

#include <string.h>
#include <errno.h>

//int main()
//{
//	//我们打印⼀下0~10这些错误码对应的信息 
//	int i = 0;
//	for (i = 0; i <= 10; i++)
//	{
//		printf("%s\n", strerror(i));
//	}
//	return 0;
//}

//C语言的库函数在运行的时候，如果发生错误，就会将错误存在一个变量中，这个变量是：errno；
//我们再来认识一个函数 fopen
//FILE * fopen ( const char * filename, const char * mode );
//如果打开文件成功，就返回一个有效的指针，如果打开文件失败，返回一个NULL指针

//int main()
//{
//	//打开文件
//	FILE* pfile = fopen("text.txt", "r");
//	if (pfile == NULL)
//	{
//		printf("打开文件失败\n");
//		return 1;
//	}
//	else
//		printf("打开文件成功\n");
//	return 0;
//}

//int main()
//{
//	//打开文件
//	FILE* pfile = fopen("text.txt", "r");
//	if (pfile == NULL)
//		printf("Error opening file text.txt: %s\n", strerror(errno));
//	
//	return 0;
//}

//int main()
//{
//	//打开文件
//	FILE* pfile = fopen("text.txt", "r");
//	if (pfile == NULL)
//	{
//		printf("Error opening file text.txt: %s\n", strerror(errno));
//		return 1;
//	}
//	//读文件
//	//关闭文件
//	fclose(pfile);
//	return 0;
//}

//除了strerror函数外,还有perror
//perror相当于将上述printf("Error opening file text.txt: %s\n", strerror(errno))完成了，直接将错误信息
//打印出来。perror函数在打印错误信息前，会先打印自定义的信息。（perror = printf + strerror）
//int main()
//{
//	FILE* pFile = fopen("unexist.ent", "r");
//	if (pFile == NULL)
//		perror("Error opening file unexist.ent");
//	return 0;
//}

//2.字符分类函数
//C语⾔中有⼀系列的函数是专⻔做字符分类的，也就是⼀个字符是属于什么类型的字符的。这些函数的使⽤都需要包含⼀个头⽂件是 ctype.h
//这些函数的使⽤⽅法⾮常类似，我们就讲解⼀个函数的实现，其他的⾮常类似：

//islower 是能够判断参数部分的 c 是否是⼩写字⺟的。通过返回值来说明是否是⼩写字⺟，如果是⼩写字⺟就返回⾮0的整数，如果不是⼩写字⺟，则返回0。
//int islower ( int c );
#include <ctype.h>
//int main()
//{
//	//int ret = islower('a');//2
//	//int ret = islower('A');//0
//	//int ret = isdigit('3');//4
//	int ret = isdigit('Q');//0
//
//
//	printf("%d\n", ret);
//	return 0;
//}

//写⼀个代码，将字符串中的⼩写字⺟转⼤写，其他字符不变。
//int main()
//{
//	int i = 0;
//	char str[] = "Test String.\n";
//	char c;
//	while (str[i])
//	{
//		c = str[i];
//		if (islower(c))
//			c -= 32;//在 ASCII 编码中，小写字母和大写字母的差值为 32
//		putchar(c);//将转换后的大写字符逐个输出，最终形成全大写的字符串
//		i++;
//	}
//	return 0;
//}

//3.字符转换函数
//C语⾔提供了2个字符转换函数：
//int tolower ( int c ); //将参数传进去的⼤写字⺟转⼩写  
//int toupper(int c); //将参数传进去的⼩写字⺟转⼤写 

//int main()
//{
//	//int ret = toupper('a');
//	//printf("%c\n", ret);
//	
//	printf("%c\n", toupper('a'));
//	printf("%c\n", tolower('A'));
//
//	return 0;
//}

//上⾯的代码，我们将⼩写转⼤写，是-32完成的效果，有了转换函数，就可以直接使⽤ tolower 函数。
//int main()
//{
//	int i = 0;
//	char str[] = "Test String.\n";
//	char c;
//	while (str[i])
//	{
//		c = str[i];
//		if (islower(c))
//			c = toupper(c);
//		putchar(c);
//		i++;
//	}
//	return 0;
//}
//int main()
//{
//	int i = 0;
//	char str[] = "Test String.\n";
//	
//	while (str[i])
//	{
//		if (islower(str[i]))
//			printf("%c", toupper(str[i]));
//		else
//			printf("%c", str[i]);
//		i++;
//	}
//	return 0;
//}
//int main()
//{
//	int i = 0;
//	char str[] = "Test String.\n";
//
//	while (str[i])
//	{
//		if (islower(str[i]))
//		{
//			str[i] = toupper(str[i]);
//		}
//		printf("%c", str[i]);
//		i++;
//	}
//	return 0;
//}

//4.C语言内存函数
//前面我们学习的字符串函数只是针对于字符串的，对于其他的类型，如int等，是无法使用的，而内存函数，就可以对任何的类型进行使用 

//4.1  memcpy（内存拷贝函数）
//void * memcpy ( void * destination, const void * source, size_t num );
//可以从src的位置开始向后拷贝（复制）num个字节的数据到dest指向的内存位置，遇到 '\0' 的时候并不会停下来。如果source和destination有
// 任何的重叠，复制的结果都是未定义的。对于重叠的内存，交给memmove来处理。
//int main()
//{
//	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int arr2[10] = { 0 };
//	//内存拷贝
//	//memcpy(arr2, arr1, 20);//要拷贝前5个整数，一共有20个字节
//	//memcpy(arr2, arr1+2, 20);
//	memcpy(arr2, arr1 + 2, 17);
//	//03 00 00 00 04 00 00 00 05 00 00 00 06 00 00 00 07 00 00 00 --- 此时只是拷贝17个字节，刚好访问到 07 的位置上，因此还是可以打印出来
//	//3 4 5 6 7
//
// 	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr2[i]);
//	}
//	return 0;
//}

//memcpy模拟实现
#include <assert.h>
//void* my_memcpy(void* dest, const void* src, size_t num)
//{
//	assert(dest && src);
//	void* ret = dest;
//	while (num--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
//	return ret;
//}
//int main()
//{
//	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//	int arr2[10] = { 0 };
//	//内存拷贝
//	my_memcpy(arr2, arr1 + 2, 20);
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr2[i]);
//	}
//	return 0;
//}

//int main()
//{
//	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//	
//	my_memcpy(arr1 + 2, arr1, 20);
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr1[i]);//重叠，会打印 1 2 1 2 1 2 1 8 9 10
//	}				            
//	return 0;
//}

//4.2  memmove（内存移动函数）
//可以实现重叠内存的拷贝
//void * memmove ( void * destination, const void * source, size_t num );
//int main()
//{
//	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//	memmove(arr1 + 2, arr1, 20);
//	//memmove(arr1, arr1 + 2, 20);
//
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr1[i]);//1 2 1 2 3 4 5 8 9 10
//	}
//	return 0;
//}

//memmove模拟实现
//void* my_memmove(void* dest, void* src, size_t num)
//{
//	void* ret = dest;
//	assert(dest && src);
//	if (dest < src)
//	{
//		//前->后
//		while (num--)
//		{
//			*(char*)dest = *(char*)src;
//			dest = (char*)dest + 1;
//			src = (char*)src + 1;
//		}
//	}
//	else
//	{
//		//后->前
//		while (num--)
//		{
//			*((char*)dest + num) = *((char*)src + num);//将来源空间的最后一个字节放到目标空间的最后一个字节上(num=20在while的判断条件
//		}											  // 那里用完后，--变成19，1+19 = 20)；以此类推，放完最后一个字节放倒数第二个字节
//	}												 // 直到num=0,停止循环
//	return ret;
//}
//int main()
//{
//	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//	my_memmove(arr1 + 2, arr1, 20);
//	//my_memmove(arr1, arr1 + 2, 20);
//
//
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr1[i]);//1 2 1 2 3 4 5 8 9 10
//	}
//	return 0;
//}

//4.3 memcmp（内存比较函数）
//int memcmp ( const void * ptr1, const void * ptr2, size_t num );
//⽐较从ptr1和ptr2指针指向的位置开始，向后的num个字节；ptr1>ptr2,返回大于0的数字、ptr1<ptr2，返回小于0的数字、ptr1=ptr2,返回0
//int main()
//{
//	int arr1[] = { 1,2,4 };//01 00 00 00 02 00 00 00 04 00 00 00
//	int arr2[] = { 1,2,3 };//01 00 00 00 02 00 00 00 03 00 00 00
//	int ret = memcmp(arr1, arr2, 9);
//	printf("%d\n", ret);
//	return 0;
//}

//memcmp模拟实现
//int my_memcmp(const void* p1, const void* p2, size_t num)
//{
//	assert(p1 && p2);
//	while (num--)
//	{
//		if (*(char*)p1 != *(char*)p2)
//			return *(char*)p1 - *(char*)p2;
//		((char*)p1)++;
//		((char*)p2)++;
//		//p1 = (char*)p1 + 1;
//		//p2 = (char*)p2 + 1;
//	}
//	return 0;
//}
//int main()
//{
//	int arr1[] = { 1,2,5 };//01 00 00 00 02 00 00 00 05 00 00 00
//	int arr2[] = { 1,2,3 };//01 00 00 00 02 00 00 00 03 00 00 00
//	int ret = my_memcmp(arr1, arr2, 9);
//	printf("%d\n", ret);
//	return 0;
//}

//4.4 memset（内存设置函数）
//void * memset ( void * ptr, int value, size_t num );
//memset是⽤来设置内存的，将内存中的值以字节为单位设置成想要的内容。
//int main()
//{
//	char arr[] = "hello world";
//	memset(arr, 'x', 5);//但将前五个字符设置为0时，打印时没有显示任何内容，因为0就是‘\0’
//	printf("%s\n", arr);
//	memset(arr + 6, 'y', 5);
//	printf("%s\n", arr);
//	return 0;
//}

//int main()
//{
//	int arr[10] = { 0 };
//	memset(arr, 1, 40);//我们是想把数组中10个数都改成1，但是实际执行起来时错误的，因为该函数是以字节为单位来设置内存的，相当于将
//	                   //40个字节的每一个字节都设置成了1
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//	return 0;
//}
//所以我们一般使用memset都是想将数组中的数都改成0
//int main()
//{
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	memset(arr, 0, 40);
//	int i = 0;
//	for (i = 0; i < 10; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//}